package com.kaisu.blog.infrastructure.blog.repository.impl;

import cn.hutool.core.util.StrUtil;
import com.kaisu.blog.common.exceptions.InternalException;
import com.kaisu.blog.domain.blog.Blog;
import com.kaisu.blog.domain.blog.Comment;
import com.kaisu.blog.domain.blog.enums.BlogStatus;
import com.kaisu.blog.domain.blog.repostiroy.BlogRepository;
import com.kaisu.blog.infrastructure.blog.BlogPO;
import com.kaisu.blog.infrastructure.blog.convert.BlogConvert;
import com.kaisu.blog.infrastructure.blog.dao.BlogPODAO;
import com.kaisu.blog.infrastructure.user.UserPO;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.Predicate;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author kaisu
 */
@Repository
@RequiredArgsConstructor
public class BlogRepositoryImpl implements BlogRepository {
    private final BlogConvert convert;
    private final BlogPODAO blogPODAO;

    @Override
    public void save(Blog blog) {
        blogPODAO.save(convert.toPO(blog));
    }

    @Override
    public Blog findById(String id) {
        return blogPODAO.findById(id).map(convert::toDomain).orElseThrow(() -> new InternalException("未获取到博客！"));
    }

    @Override
    public void removeByIds(Collection<String> id) {
        blogPODAO.deleteAllById(id);
    }

    @Override
    public Page<Blog> pageByUserId(String userId, Pageable pageable) {
        Page<BlogPO> page = blogPODAO.findByCreateUserIdInAndStatus(Collections.singleton(userId), BlogStatus.PUBLISHED.name(), pageable);
        return page.map(convert::toDomain);
    }

    @Override
    public Page<Blog> pageInUserIds(Collection<String> userIds, Pageable pageable) {
        Page<BlogPO> page = blogPODAO.findByCreateUserIdInAndStatus(userIds, BlogStatus.PUBLISHED.name(), pageable);
        return page.map(convert::toDomain);
    }

    @Override
    public Page<Blog> myBlogPage(String userId, Pageable pageable) {
        Page<BlogPO> page = blogPODAO.findByCreateUserId(userId, pageable);
        return page.map(convert::toDomain);
    }

    @Override
    public Page<Comment> commentPage(String id, Pageable pageable) {
        BlogPO blogPO = blogPODAO.findById(id).orElseThrow(() -> new InternalException("未获取到博客！"));
        Set<Comment> comments = blogPO.getComments().stream().map(convert::toDomain).collect(Collectors.toSet());
        return new PageImpl<>(new ArrayList<>(comments), pageable, comments.size());
    }

    @Override
    public Page<Blog> blogPageBySearch(String key, Pageable pageable) {
        Page<BlogPO> page = blogPODAO.findAll((root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (StrUtil.isNotEmpty(key)) {
                Join<UserPO, BlogPO> cronJobTask = root.join("createUser");
                Predicate taskNamePredicate = criteriaBuilder.like(cronJobTask.get("nickname"), "%" + key + "%");
                Predicate titlePredicate = criteriaBuilder.like(root.get("title"), "%" + key + "%");
                predicates.add(taskNamePredicate);
                predicates.add(titlePredicate);
            }
            Predicate statusPredicate = criteriaBuilder.equal(root.get("status"), BlogStatus.PUBLISHED.name());
            if (predicates.isEmpty()) {
                return criteriaBuilder.and(statusPredicate);
            } else {
                return criteriaBuilder.and(statusPredicate, criteriaBuilder.or(predicates.toArray(new Predicate[0])));
            }
        }, pageable);
        return page.map(convert::toDomain);
    }

    @Override
    public Page<Blog> page(String currentUserId, Pageable pageable) {
        Page<BlogPO> page = blogPODAO.findAll((root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (StrUtil.isNotEmpty(currentUserId)) {
                Join<UserPO, BlogPO> cronJobTask = root.join("createUser");
                Predicate idPredicate = criteriaBuilder.notEqual(cronJobTask.get("id"), currentUserId);
                predicates.add(idPredicate);
            }
            Predicate statusPredicate = criteriaBuilder.equal(root.get("status"), BlogStatus.PUBLISHED.name());
            if (predicates.isEmpty()) {
                return criteriaBuilder.and(statusPredicate);
            } else {
                return criteriaBuilder.and(statusPredicate, criteriaBuilder.or(predicates.toArray(new Predicate[0])));
            }
        }, pageable);
        return page.map(convert::toDomain);
    }
}
